import asyncio
import subprocess
import threading
import time
import tkinter as tk
import winreg
from tkinter import ttk, scrolledtext, messagebox

from bleak import BleakScanner, BleakClient
from bleak.backends.device import BLEDevice
from bleak.backends.scanner import AdvertisementData
from ttkbootstrap import Frame, Button, Label, Treeview
from ttkbootstrap.constants import *

from app.util.annotation_util import annotate, ClassInfo
from app.view.base_view import View


@annotate(ClassInfo(name="BleConnector", category="dev_tool"))
class BleConnectorView(View):
    def __init__(self, parent, controller):
        super().__init__(parent, controller)
        # 状态变量
        self.is_scanning = False
        self.scanner = None
        self.connected_device = None
        self.connected_client = None
        self.discovered_devices = {}  # 存储扫描到的设备
        self.device_uuids = {}  # 存储设备的UUID信息

        # Name过滤变量
        self.name_filter_var = tk.StringVar()
        self.name_filter_var.trace_add("write", self.on_name_filter_var_change)

        # 创建界面
        self._create_widgets()

        # 检查蓝牙状态
        self.check_bluetooth_status()

        # 启动事件循环线程
        self.loop = asyncio.new_event_loop()
        self.loop_thread = threading.Thread(target=self.run_event_loop, daemon=True)
        self.loop_thread.start()

        # 确保窗口关闭时正确清理
        # self.protocol("WM_DELETE_WINDOW", self.on_close)

    def run_event_loop(self):
        """运行asyncio事件循环的线程"""
        asyncio.set_event_loop(self.loop)
        self.loop.run_forever()

    def submit_to_loop(self, coro):
        """将协程提交到事件循环"""
        return asyncio.run_coroutine_threadsafe(coro, self.loop)

    def on_name_filter_var_change(self):
        pass

    def _create_widgets(self):
        """创建界面组件"""
        # 顶部控制栏 - 包含蓝牙状态、扫描和清除按钮
        top_frame = Frame(self, padding=10)
        top_frame.pack(fill=X)

        # 蓝牙状态
        self.bluetooth_status_var = tk.StringVar(value="蓝牙状态: 检测中...")
        bluetooth_status_label = Label(top_frame, textvariable=self.bluetooth_status_var,
                                       font=("微软雅黑", 10, "bold"))
        bluetooth_status_label.pack(side=LEFT, padx=10)

        # 扫描和清除按钮
        control_frame = Frame(top_frame)
        control_frame.pack(side=RIGHT, padx=10)

        self.scan_btn = Button(control_frame, text="开始扫描", command=self.toggle_scan, style=SUCCESS)
        self.scan_btn.pack(side=LEFT, padx=5)

        self.clear_btn = Button(control_frame, text="清空列表", command=self.clear_devices, style=OUTLINE)
        self.clear_btn.pack(side=LEFT, padx=5)

        # 主内容区 - 填充剩余空间
        main_frame = Frame(self, padding=(10, 0, 10, 10))
        main_frame.pack(fill=BOTH, expand=YES)

        # 设备列表区域 - 铺满X方向
        device_frame = Frame(main_frame)
        device_frame.pack(fill=BOTH, expand=YES, pady=(0, 10))

        Label(device_frame, text="扫描到的设备:").pack(anchor=W, pady=(0, 5))

        # 设备列表和操作按钮组合
        device_content_frame = Frame(device_frame)
        device_content_frame.pack(fill=BOTH, expand=YES)

        # 设备列表
        columns = ("name", "address", "connectable", "rssi")
        self.device_tree = Treeview(device_content_frame, columns=columns, show="headings")
        self.device_tree.heading("name", text="设备名称")
        self.device_tree.heading("address", text="MAC地址")
        self.device_tree.heading("connectable", text="可连接")
        self.device_tree.heading("rssi", text="信号强度 (dBm)")

        self.device_tree.column("name", width=150, stretch=YES)
        self.device_tree.column("address", width=180, stretch=YES)
        self.device_tree.column("connectable", width=80, anchor=CENTER)
        self.device_tree.column("rssi", width=100, anchor=CENTER)

        self.device_tree.pack(side=LEFT, fill=BOTH, expand=YES)

        # 设备列表滚动条
        device_scrollbar = ttk.Scrollbar(device_content_frame, orient=VERTICAL, command=self.device_tree.yview)
        device_scrollbar.pack(side=RIGHT, fill=Y)
        self.device_tree.configure(yscrollcommand=device_scrollbar.set)

        # 设备操作按钮区域
        device_actions = Frame(device_frame)
        device_actions.pack(fill=X, pady=5)

        # IP搜索栏
        name_filter_frame = ttk.Frame(device_actions)
        # MAC地址输入
        ttk.Label(name_filter_frame, text="IP过滤:").pack(side=LEFT)
        ttk.Entry(name_filter_frame, textvariable=self.name_filter_var, width=50).pack(side=RIGHT, fill=X)
        name_filter_frame.pack(side=LEFT, pady=5)

        self.connect_btn = Button(device_actions, text="连接设备", command=self.connect_device, state=DISABLED)
        self.connect_btn.pack(side=LEFT, padx=5)

        self.disconnect_btn = Button(device_actions, text="断开连接", command=self.disconnect_device, state=DISABLED)
        self.disconnect_btn.pack(side=LEFT, padx=5)

        self.view_data_btn = Button(device_actions, text="查看原始数据", command=self.view_raw_data, state=DISABLED)
        self.view_data_btn.pack(side=LEFT, padx=5)

        # 中间区域 - 分为UUID列表和数据发送区
        middle_frame = Frame(main_frame)
        middle_frame.pack(fill=BOTH, expand=YES, pady=(0, 10))

        # 左侧UUID列表
        uuid_frame = Frame(middle_frame, width=600)
        uuid_frame.pack(side=LEFT, fill=BOTH, expand=YES, padx=(0, 10))

        Label(uuid_frame, text="设备UUID列表:").pack(anchor=W, pady=(0, 5))

        uuid_content_frame = Frame(uuid_frame)
        uuid_content_frame.pack(fill=BOTH, expand=YES)

        uuid_columns = ("uuid", "name", "properties")
        self.uuid_tree = Treeview(uuid_content_frame, columns=uuid_columns, show="headings")
        self.uuid_tree.heading("uuid", text="UUID")
        self.uuid_tree.heading("name", text="名称")
        self.uuid_tree.heading("properties", text="属性")

        self.uuid_tree.column("uuid", width=300, stretch=YES)
        self.uuid_tree.column("name", width=150, stretch=YES)
        self.uuid_tree.column("properties", width=100)

        self.uuid_tree.pack(side=LEFT, fill=BOTH, expand=YES)

        # UUID滚动条
        uuid_scrollbar = ttk.Scrollbar(uuid_content_frame, orient=VERTICAL, command=self.uuid_tree.yview)
        uuid_scrollbar.pack(side=RIGHT, fill=Y)
        self.uuid_tree.configure(yscrollcommand=uuid_scrollbar.set)

        # 右侧数据发送区
        send_frame = Frame(middle_frame, width=400)
        send_frame.pack(side=RIGHT, fill=BOTH, expand=YES, padx=(10, 0))

        Label(send_frame, text="发送数据:").pack(anchor=W, pady=(0, 5))

        self.data_entry = scrolledtext.ScrolledText(send_frame, height=5, wrap=tk.WORD)
        self.data_entry.pack(fill=X, pady=(0, 10))

        # 格式选择
        format_frame = Frame(send_frame)
        format_frame.pack(fill=X, pady=(0, 10))

        self.data_format = tk.StringVar(value="ascii")
        ascii_radio = ttk.Radiobutton(format_frame, text="ASCII", variable=self.data_format, value="ascii")
        ascii_radio.pack(side=LEFT, padx=10)

        hex_radio = ttk.Radiobutton(format_frame, text="HEX", variable=self.data_format, value="hex")
        hex_radio.pack(side=LEFT, padx=10)

        # 发送按钮
        self.send_btn = Button(send_frame, text="发送数据", command=self.send_data, state=DISABLED)
        self.send_btn.pack(fill=X, pady=(0, 10))

        # 底部日志区域 - 铺满X方向
        log_frame = Frame(self, padding=(10, 0, 10, 10))
        log_frame.pack(fill=BOTH, expand=YES, side=BOTTOM)

        Label(log_frame, text="操作日志:").pack(anchor=W, pady=(0, 5))

        log_content_frame = Frame(log_frame)
        log_content_frame.pack(fill=X, expand=YES)

        self.log_text = scrolledtext.ScrolledText(log_content_frame, wrap=tk.WORD, state=DISABLED)
        self.log_text.pack(side=LEFT, fill=X, expand=YES)

        # 绑定事件
        self.device_tree.bind("<<TreeviewSelect>>", self.on_device_select)
        self.uuid_tree.bind("<<TreeviewSelect>>", self.on_uuid_select)

    def log(self, message):
        """添加日志信息"""
        timestamp = time.strftime("%H:%M:%S")
        self.log_text.configure(state=NORMAL)
        self.log_text.insert(END, f"[{timestamp}] {message}\n")
        self.log_text.see(END)
        self.log_text.configure(state=DISABLED)

    def check_bluetooth_status(self):
        """检查蓝牙状态"""
        try:
            # 检查注册表中的蓝牙服务状态
            reg_path = r"SYSTEM\CurrentControlSet\Services\BTHPORT"
            reg_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, reg_path, 0, winreg.KEY_READ)
            status, _ = winreg.QueryValueEx(reg_key, "Start")
            winreg.CloseKey(reg_key)

            # 检查是否有蓝牙适配器
            adapters_reg_path = r"SYSTEM\CurrentControlSet\Control\Class\{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}"
            adapters_key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, adapters_reg_path, 0, winreg.KEY_READ)

            has_adapters = False
            try:
                index = 0
                while True:
                    winreg.EnumKey(adapters_key, index)
                    has_adapters = True
                    index += 1
            except WindowsError:
                pass
            winreg.CloseKey(adapters_key)

            if not has_adapters:
                self.bluetooth_status_var.set("蓝牙状态: 未检测到适配器")
                self.scan_btn.configure(state=DISABLED)
                self.log("未检测到蓝牙适配器")
                return False

            # 检查PowerShell中的实际状态
            command = 'Get-PnpDevice -Class Bluetooth | Where-Object { $_.Status -eq "OK" } | Select-Object -First 1'
            result = subprocess.run(
                ["powershell", "-Command", command],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                timeout=10
            )

            if "OK" in result.stdout and "Bluetooth" in result.stdout:
                self.bluetooth_enabled = True
                self.bluetooth_status_var.set("蓝牙状态: 已启用")
                self.scan_btn.configure(state=NORMAL)
                self.log("蓝牙已启用")
                return True
            else:
                self.bluetooth_enabled = False
                self.bluetooth_status_var.set("蓝牙状态: 已禁用")
                self.scan_btn.configure(state=DISABLED)
                self.log("蓝牙已禁用，请手动开启")
                return False

        except Exception as e:
            self.log(f"蓝牙状态检查失败: {str(e)}")
            self.bluetooth_status_var.set(f"蓝牙状态: 检查失败")
            self.scan_btn.configure(state=DISABLED)
            return False

    def toggle_scan(self):
        """开始或停止扫描设备"""
        if not self.bluetooth_enabled:
            messagebox.showwarning("警告", "请先开启蓝牙")
            return

        if self.is_scanning:
            self.stop_scan()
        else:
            self.start_scan()

    def start_scan(self):
        """开始扫描BLE设备"""
        self.is_scanning = True
        self.scan_btn.configure(text="停止扫描")
        self.log("开始扫描BLE设备...")

        # 清除现有设备
        self.discovered_devices.clear()
        for item in self.device_tree.get_children():
            self.device_tree.delete(item)

        # 启动扫描
        self.submit_to_loop(self._async_scan())

    async def _async_scan(self):
        """异步扫描设备的函数"""
        self.scanner = BleakScanner(detection_callback=self.detection_callback)
        await self.scanner.start()

        # 扫描持续进行，直到调用stop_scan
        while self.is_scanning:
            await asyncio.sleep(1)

        await self.scanner.stop()

    def stop_scan(self):
        """停止扫描设备"""
        self.is_scanning = False
        self.scan_btn.configure(text="开始扫描")
        self.log(f"扫描已停止，共发现 {len(self.discovered_devices)} 个设备")

    def detection_callback(self, device: BLEDevice, advertisement_data: AdvertisementData):
        """设备发现回调函数"""
        # 存储设备信息
        device_id = device.address
        if device_id not in self.discovered_devices:
            self.discovered_devices[device_id] = {
                "device": device,
                "advertisement_data": advertisement_data,
                "connectable": getattr(advertisement_data, 'connectable',
                                       getattr(advertisement_data, 'is_connectable', False)),
                "rssi": advertisement_data.rssi
            }

            # 在UI线程中更新设备列表
            self.after(0, self.update_device_list)

        # 更新已发现设备的RSSI
        else:
            self.discovered_devices[device_id]["rssi"] = advertisement_data.rssi
            self.after(0, self.update_device_rssi, device_id, advertisement_data.rssi)

    def update_device_list(self):
        """更新设备列表UI"""
        # 先清空现有项
        for item in self.device_tree.get_children():
            self.device_tree.delete(item)

        # 添加所有设备
        for device_id, info in list(self.discovered_devices.items()):
            device = info["device"]
            name = device.name or "未知设备"
            connectable = "是" if info["connectable"] else "否"

            self.device_tree.insert("", END, values=(
                name,
                device_id,
                connectable,
                info["rssi"]
            ))

    def update_device_rssi(self, device_id, rssi):
        """更新设备的信号强度"""
        for item in self.device_tree.get_children():
            if self.device_tree.item(item, "values")[1] == device_id:
                values = list(self.device_tree.item(item, "values"))
                values[3] = rssi
                self.device_tree.item(item, values=values)
                break

    def clear_devices(self):
        """清空设备列表"""
        self.discovered_devices.clear()
        for item in self.device_tree.get_children():
            self.device_tree.delete(item)
        self.log("设备列表已清空")

    def on_device_select(self, event):
        """设备选中事件处理"""
        selected_items = self.device_tree.selection()
        if selected_items:
            # 启用设备操作按钮
            self.connect_btn.configure(state=NORMAL)
            self.view_data_btn.configure(state=NORMAL)

            # 如果已连接其他设备，禁用连接按钮
            if self.connected_device:
                selected_address = self.device_tree.item(selected_items[0], "values")[1]
                if selected_address == self.connected_device.address:
                    self.connect_btn.configure(state=DISABLED)
                    self.disconnect_btn.configure(state=NORMAL)
                else:
                    self.connect_btn.configure(state=NORMAL)
                    self.disconnect_btn.configure(state=DISABLED)
            else:
                self.disconnect_btn.configure(state=DISABLED)
        else:
            self.connect_btn.configure(state=DISABLED)
            self.disconnect_btn.configure(state=DISABLED)
            self.view_data_btn.configure(state=DISABLED)

    def on_uuid_select(self, event):
        """UUID选中事件处理"""
        selected_items = self.uuid_tree.selection()
        if selected_items and self.connected_client:
            # 检查UUID是否可写
            item = selected_items[0]
            properties = self.uuid_tree.item(item, "values")[2]
            if "写" in properties:
                self.send_btn.configure(state=NORMAL)
            else:
                self.send_btn.configure(state=DISABLED)
        else:
            self.send_btn.configure(state=DISABLED)

    def view_raw_data(self):
        """查看设备原始广播数据"""
        selected_items = self.device_tree.selection()
        if not selected_items:
            return

        address = self.device_tree.item(selected_items[0], "values")[1]
        device_info = self.discovered_devices.get(address)

        if not device_info:
            return

        # 创建弹窗显示原始数据
        top = tk.Toplevel(self)
        top.title(f"原始数据 - {address}")
        top.geometry("600x400")
        top.transient(self)
        top.grab_set()

        text = scrolledtext.ScrolledText(top, wrap=tk.WORD)
        text.pack(fill=BOTH, expand=YES, padx=10, pady=10)

        # 格式化原始数据
        adv_data = device_info["advertisement_data"]
        text.insert(END, "设备信息:\n")
        text.insert(END, f"  名称: {device_info['device'].name or '未知'}\n")
        text.insert(END, f"  地址: {address}\n")

        connectable = getattr(adv_data, 'connectable', getattr(adv_data, 'is_connectable', False))
        text.insert(END, f"  可连接: {'是' if connectable else '否'}\n")

        text.insert(END, f"  信号强度: {adv_data.rssi} dBm\n\n")

        text.insert(END, "原始广播数据:\n")
        if adv_data.manufacturer_data:
            text.insert(END, "  厂商数据:\n")
            for key, value in adv_data.manufacturer_data.items():
                text.insert(END, f"    0x{key:04X}: {value.hex(' ')}\n")

        if adv_data.service_data:
            text.insert(END, "  服务数据:\n")
            for key, value in adv_data.service_data.items():
                text.insert(END, f"    {key}: {value.hex(' ')}\n")

        if adv_data.service_uuids:
            text.insert(END, "  服务UUID:\n")
            for uuid in adv_data.service_uuids:
                text.insert(END, f"    {uuid}\n")

        if adv_data.manufacturer_data or adv_data.service_data:
            combined_data = b''.join(list(adv_data.manufacturer_data.values()) + list(adv_data.service_data.values()))
            text.insert(END, "\n  合并数据ASCII解析:\n")
            try:
                text.insert(END, f"    {combined_data.decode('ascii', errors='replace')}\n")
            except:
                text.insert(END, "    无法解析为ASCII\n")

        text.configure(state=DISABLED)

        # 关闭按钮
        btn = Button(top, text="关闭", command=top.destroy)
        btn.pack(pady=10)

    def connect_device(self):
        """连接选中的设备"""
        selected_items = self.device_tree.selection()
        if not selected_items:
            return

        address = self.device_tree.item(selected_items[0], "values")[1]
        device_info = self.discovered_devices.get(address)

        if not device_info:
            return

        self.log(f"正在连接设备: {address}")
        self.connected_device = device_info["device"]

        # 禁用相关按钮
        self.scan_btn.configure(state=DISABLED)
        self.connect_btn.configure(state=DISABLED)

        # 开始连接
        future = self.submit_to_loop(self._async_connect(device_info["device"]))
        future.add_done_callback(self._connect_done)

    async def _async_connect(self, device):
        """异步连接设备"""
        try:
            async with BleakClient(device) as client:
                self.connected_client = client
                self.log(f"已连接到设备: {device.address}")

                # 发现服务和特征（使用新版API）
                services = client.services
                self.device_uuids = {}

                for service in services:
                    for char in service.characteristics:
                        # 确定特征属性
                        properties = []
                        if char.properties.read:
                            properties.append("读")
                        if char.properties.write or char.properties.write_with_response:
                            properties.append("写")
                        if char.properties.notify:
                            properties.append("通知")
                        if char.properties.indicate:
                            properties.append("指示")

                        self.device_uuids[str(char.uuid)] = {
                            "name": char.description or "未知特征",
                            "properties": ", ".join(properties),
                            "characteristic": char
                        }

                # 启用通知
                for char_uuid, char_info in self.device_uuids.items():
                    if "通知" in char_info["properties"] or "指示" in char_info["properties"]:
                        await client.start_notify(char_info["characteristic"], self.notify_callback)

                # 保持连接状态
                while self.connected_client == client:
                    await asyncio.sleep(1)

                return True
        except Exception as e:
            self.log(f"连接失败: {str(e)}")
            return False

    def _connect_done(self, future):
        """连接完成回调"""
        success = future.result()
        if success:
            # 更新UI
            self.after(0, self.update_uuid_list)
            self.after(0, lambda: self.disconnect_btn.configure(state=NORMAL))
        else:
            # 连接失败，重置状态
            self.connected_device = None
            self.connected_client = None
            self.after(0, lambda: self.scan_btn.configure(state=NORMAL))
            self.after(0, lambda: self.connect_btn.configure(state=NORMAL))

    def update_uuid_list(self):
        """更新UUID列表"""
        # 清空现有项
        for item in self.uuid_tree.get_children():
            self.uuid_tree.delete(item)

        # 添加所有UUID
        for uuid, info in self.device_uuids.items():
            self.uuid_tree.insert("", END, values=(uuid, info["name"], info["properties"]))

    def notify_callback(self, sender, data):
        """通知回调函数"""
        sender_uuid = sender.uuid
        uuid_info = self.device_uuids.get(str(sender_uuid))

        if uuid_info:
            self.log(f"收到来自 {uuid_info['name']} ({sender_uuid}) 的数据:")
            self.log(f"  HEX: {data.hex(' ')}")
            try:
                self.log(f"  ASCII: {data.decode('ascii', errors='replace')}")
            except:
                self.log("  无法解析为ASCII")

    def disconnect_device(self):
        """断开连接"""
        if self.connected_client:
            self.log(f"正在断开与 {self.connected_device.address} 的连接...")
            self.connected_client = None
            self.connected_device = None

            # 清空UUID列表
            for item in self.uuid_tree.get_children():
                self.uuid_tree.delete(item)

            # 更新按钮状态
            self.send_btn.configure(state=DISABLED)
            self.disconnect_btn.configure(state=DISABLED)
            self.scan_btn.configure(state=NORMAL)

            # 重新启用连接按钮
            selected_items = self.device_tree.selection()
            if selected_items:
                self.connect_btn.configure(state=NORMAL)

    def send_data(self):
        """发送数据到选中的UUID"""
        selected_items = self.uuid_tree.selection()
        if not selected_items or not self.connected_client:
            return

        # 获取选中的UUID
        uuid = self.uuid_tree.item(selected_items[0], "values")[0]
        uuid_info = self.device_uuids.get(uuid)

        if not uuid_info or "写" not in uuid_info["properties"]:
            return

        # 获取输入的数据
        data_str = self.data_entry.get("1.0", END).strip()
        if not data_str:
            messagebox.showwarning("警告", "请输入要发送的数据")
            return

        try:
            # 根据选择的格式转换数据
            if self.data_format.get() == "hex":
                # 移除空格
                data_str = data_str.replace(" ", "")
                # 转换为字节
                data = bytes.fromhex(data_str)
            else:  # ascii
                data = data_str.encode("ascii")

            # 发送数据
            self.log(f"发送数据到 {uuid_info['name']} ({uuid}):")
            self.log(f"  HEX: {data.hex(' ')}")
            self.log(f"  ASCII: {data.decode('ascii', errors='replace')}")

            # 异步发送数据
            char = uuid_info["characteristic"]
            future = self.submit_to_loop(
                self.connected_client.write_gatt_char(char, data, response=True)
            )
            future.add_done_callback(self._send_done)

            # 清空输入框
            self.data_entry.delete("1.0", END)

        except Exception as e:
            self.log(f"数据转换失败: {str(e)}")
            messagebox.showerror("错误", f"数据格式错误: {str(e)}")

    def _send_done(self, future):
        """发送完成回调"""
        try:
            future.result()
            self.log("数据发送成功")
        except Exception as e:
            self.log(f"数据发送失败: {str(e)}")

    def on_close(self):
        """窗口关闭处理"""
        # 停止扫描
        self.is_scanning = False

        # 断开连接
        if self.connected_client:
            self.connected_client = None

        # 停止事件循环
        self.loop.call_soon_threadsafe(self.loop.stop)
        self.loop_thread.join()

        # 关闭窗口
        self.destroy()

    def create_widgets(self):
        pass
